package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"time"

	influxdb2 "github.com/influxdata/influxdb-client-go/v2"
	"github.com/influxdata/influxdb-client-go/v2/api"
)

var influxClient influxdb2.Client
var influxWriteAPI api.WriteAPI

func initInfluxDB() {
	url := "http://localhost:16902"
	token := "nASrTCzt1iBrIG67BPC7SywgwfLXnG31Ni-YAufCNrRBJJGW-Uk9RUJ75v9CWMWznPVTaC30eDi28i_f_rCw7A=="
	org := "hitwh"
	bucket := "gps_location"

	influxClient = influxdb2.NewClient(url, token)

	if influxClient == nil {
		log.Panic("failed to open influxdb!\n")
	}

	influxWriteAPI = influxClient.WriteAPI(org, bucket)
}

// 保存着每个中断id的消息流水号
var SerialNumMap map[string]uint16

// jt808的二进制位是采用的大端序
type JT808Header struct {
	RawData         []byte
	MsgId           uint16  // 消息id byte[0-1]
	BodyPropRawData [2]byte // 消息体属性原始数据 byte[2-3]

	// -------消息体属性的2个字节分解后的数据-----------
	// 2 bits 被保留      bit[15-14]

	HasSubPkg   bool   // bit[13]    1 bits 是否分包
	EncryptMode uint8  // bit[12-10] 3 bits 数据加密方式
	BodyLen     uint16 // bit[9-0]   10 bits 消息体长度
	// ------------------------------------

	// 终端手机号或设备ID, 6byte的8421BCD码, 12位数字
	// byte[4-9]
	DeviceId string

	// 消息流水号 2bytes
	SerialNum uint16 // byte[10-11]

	// ---- byte[12-15] 消息包封装项 ----
	TotalPkg uint16 // 消息包总数 byte[12-13]
	PkgNum   uint16 // 包序号 byte[14-15]
	// --------------------------------
}

type JT808Msg struct {
	RawData     []byte
	Header      *JT808Header
	BodyRawData []byte
}

type JT808LocationMsg struct {
	JT808Msg

	// 消息体 byte[0-3] 报警标志 4 bytes
	// 消息体 byte[4-7] 状态位标志 4 bytes
	// 消息体 byte[8-11] 纬度 4 bytes
	// 消息体 byte[12-15] 经度 4 bytes
	// 消息体 byte[16-17] 海拔高度 2 bytes
	// 消息体 byte[18-19] 速度 2 bytes
	// 消息体 byte[20-21] 方向 2 bytes
	// 消息体 byte[22-27] GPS时间 6 bytes

	// 消息体剩余还可能有厂家自定义的附加信息

	// 纬度
	Latitude uint32

	// 经度
	Longitude uint32
}

type JT808TermRegisterMsg struct {
	JT808Msg

	ProvinceId     uint16 // 省域id
	CityId         uint16 // 市县区id
	ManufacturerId string // 制造商id
	TermModel      string // 终端型号
	TermId         string // 终端id
	PlateColor     byte   // 车牌颜色
	PlateNumber    string // 车牌号码
}

func Write2Influx(writeAPI *api.WriteAPI, msg *JT808LocationMsg) {
	point := influxdb2.NewPointWithMeasurement("position").
		AddTag("device_id", msg.Header.DeviceId).
		AddField("latitude", msg.Latitude).
		AddField("longitude", msg.Longitude).
		SetTime(time.Now())

	(*writeAPI).WritePoint(point)
}

// 解析消息体属性（注：必须保证 header 的 bodyPropRaw 字段已经有数据，字节内是大端序）
func DecodeBodyProp(header *JT808Header) {
	// 字节0的bit[6-7]保留

	// 解析是否分包 bit[13]
	// 字节0取 5 号字节
	hsp := (header.BodyPropRawData[0] >> 5) & 0x01
	if hsp == 0 {
		header.HasSubPkg = false
	} else {
		header.HasSubPkg = true
	}

	// 解析数据加密方式 bit[10-12]
	// 字节0的 2 3 4 号字节
	header.EncryptMode = (header.BodyPropRawData[0] >> 2) & 0x07

	// 解析消息体长度 bit[0-9]
	// 字节0最后两位，字节1全都要
	header.BodyLen = (uint16(header.BodyPropRawData[0])&0x03)<<8 + uint16(header.BodyPropRawData[1])
}

func DecodeJT808Header(data []byte) *JT808Header {
	var header JT808Header

	// 解析消息id byte[0-1]
	header.MsgId = uint16(data[0])<<8 + uint16(data[1])

	// 保存消息体属性原始数据 byte[2-3]
	header.BodyPropRawData[0] = data[2]
	header.BodyPropRawData[1] = data[3]

	DecodeBodyProp(&header)

	// 解析终端手机号 byte[4-9] 总共6个字节
	for i := 4; i <= 9; i++ {
		high4 := (data[i] & 0xf0) >> 4
		low4 := data[i] & 0x0f
		header.DeviceId = header.DeviceId + string(high4+'0') + string(low4+'0')
	}

	// if header.DeviceId[0] == '0' {
	// 	header.DeviceId = header.DeviceId[1:]
	// }

	// 解析消息流水号 byte[10-11]
	header.SerialNum = uint16(data[10])&0xff + uint16(data[11])&0xff

	// 解析消息包封装项 byte[12-15](如果有的话)
	if header.HasSubPkg {
		// 解析消息包总数 byte[12-13]
		header.TotalPkg = uint16(data[12])&0xff + uint16(data[13])&0xff

		// 解析包序号 byte[14-15]
		header.PkgNum = uint16(data[14])&0xff + uint16(data[15])&0xff
		header.RawData = data[0:16]
	} else {
		header.RawData = data[0:12]
	}

	return &header
}

func EncodeJT808Header(header *JT808Header) []byte {
	var ret []byte

	if header.HasSubPkg {
		ret = make([]byte, 16)
	} else {
		ret = make([]byte, 12)
	}

	ret[0] = byte(header.MsgId >> 8)
	ret[1] = byte(header.MsgId & 0xff)

	// 消息体属性
	if header.HasSubPkg {
		ret[2] |= 0b00100000
	}

	ret[2] |= header.EncryptMode << 2 // 加密方式

	ret[2] |= byte((header.BodyLen >> 8) & 0x01)
	ret[3] |= byte(header.BodyLen & 0xff)

	// byte[4-9] 终端手机号或设备id

	j := 0
	for i := 4; i <= 9; i++ {
		high4 := header.DeviceId[j] - '0'
		low4 := header.DeviceId[j+1] - '0'

		ret[i] = (high4 << 4) + low4
		j += 2
	}

	ret[10] = byte(header.SerialNum >> 8)
	ret[11] = byte(header.SerialNum & 0xff)

	// 消息包封装项
	if header.HasSubPkg {
		// 消息包总数
		ret[12] = byte(header.TotalPkg >> 8)
		ret[13] = byte(header.TotalPkg & 0xff)

		// 包序号
		ret[14] = byte(header.PkgNum >> 8)
		ret[15] = byte(header.PkgNum & 0xff)
	}

	return ret
}

func HandleLocationReport(msg *JT808Msg, writer *bufio.Writer) {
	var localtionMsg JT808LocationMsg

	localtionMsg.Header = msg.Header
	localtionMsg.BodyRawData = msg.BodyRawData
	localtionMsg.Latitude = uint32(msg.BodyRawData[8])<<24 + uint32(msg.BodyRawData[9])<<16 + uint32(msg.BodyRawData[10])<<8 + uint32(msg.BodyRawData[11])
	localtionMsg.Longitude = uint32(msg.BodyRawData[12])<<24 + uint32(msg.BodyRawData[13])<<16 + uint32(msg.BodyRawData[14])<<8 + uint32(msg.BodyRawData[15])

	fmt.Printf("\nlatitude = %d; longitude = %d\n", localtionMsg.Latitude, localtionMsg.Longitude)

	Write2Influx(&influxWriteAPI, &localtionMsg)
}

// 将原始数据转义并加上头和校验码
func MakeJT808Data(rawData []byte) []byte {
	checkCode := rawData[0]

	for i := 1; i < len(rawData); i++ {
		checkCode ^= rawData[i]
	}

	rawData = append(rawData, checkCode)

	data := make([]byte, 1)
	data[0] = 0x7e

	for i := 0; i < len(rawData); i++ {
		// 转义
		if rawData[i] == 0x7e {
			data = append(data, 0x7d)
			data = append(data, 0x02)
		} else if rawData[i] == 0x7d {
			data = append(data, 0x7d)
			data = append(data, 0x01)
		} else {
			data = append(data, rawData[i])
		}
	}

	data = append(data, 0x7e)

	return data
}

func SendGeneralYes(header *JT808Header, writer *bufio.Writer) {
	bodyData := make([]byte, 5)
	// 应答流水号
	bodyData[0] = byte(header.SerialNum >> 8)
	bodyData[1] = byte(header.SerialNum & 0x00ff)
	// 消息id
	bodyData[2] = byte(header.MsgId >> 8)
	bodyData[3] = byte(header.MsgId & 0x00ff)
	// 结果
	bodyData[4] = 0

	var retHeader JT808Header

	retHeader.MsgId = 0x8001 // 平台通用应答
	retHeader.HasSubPkg = false
	retHeader.EncryptMode = 0
	retHeader.BodyLen = uint16(len(bodyData))
	retHeader.DeviceId = header.DeviceId
	retHeader.SerialNum = header.SerialNum

	headerData := EncodeJT808Header(&retHeader)

	retData := headerData
	retData = append(retData, bodyData...)

	retData = MakeJT808Data(retData)

	fmt.Println("Server return:")
	PrintByteArray(retData)
	fmt.Println("********************")
	fmt.Println()

	writer.Write(retData)
	writer.Flush()
}

func SetSerialNum(header *JT808Header) {
	serialNum, ok := SerialNumMap[header.DeviceId]

	if ok {
		serialNum++
		header.SerialNum = serialNum
		SerialNumMap[header.DeviceId] = serialNum
	} else {
		SerialNumMap[header.DeviceId] = 0
	}
}

// 根据消息头生成校验码
func GenAuthCode(header *JT808Header) (ret []byte) {

	for i := 0; i < len(header.DeviceId); i++ {
		ret = append(ret, ((uint8(3) << 4) + header.DeviceId[i] - '0'))
	}

	return
}

func TermRegister(header *JT808Header, writer *bufio.Writer) {
	bodyData := make([]byte, 3)

	// 应答流水号
	bodyData[0] = byte(header.SerialNum >> 8)
	bodyData[1] = byte(header.SerialNum & 0x00ff)

	// 结果
	bodyData[2] = 0

	// 鉴权码，以0结尾
	authCode := GenAuthCode(header)
	bodyData = append(bodyData, authCode...)

	var retHeader JT808Header

	retHeader.MsgId = 0x8100 // 终端注册应答
	retHeader.HasSubPkg = false
	retHeader.EncryptMode = 0
	retHeader.BodyLen = uint16(len(bodyData))
	retHeader.DeviceId = header.DeviceId
	retHeader.SerialNum = header.SerialNum

	headerData := EncodeJT808Header(&retHeader)

	retData := headerData
	retData = append(retData, bodyData...)

	retData = MakeJT808Data(retData)

	fmt.Println("Server return:")
	PrintByteArray(retData)
	fmt.Println("********************")
	fmt.Println()

	writer.Write(retData)
	writer.Flush()
}

func HandleJT808Msg(msg *JT808Msg, conn *net.Conn) {
	writer := bufio.NewWriter(*conn)
	msg.PrintMsg()

	switch msg.Header.MsgId {
	case 0x0100: // 终端注册
		TermRegister(msg.Header, writer)
	case 0x0102: // 终端鉴权
		SendGeneralYes(msg.Header, writer)
	case 0x0002: // 终端心跳
		SendGeneralYes(msg.Header, writer)
	case 0x0200: // 位置信息汇报
		HandleLocationReport(msg, writer)
	default:
		SendGeneralYes(msg.Header, writer)
	}
}

func (msg *JT808Msg) PrintMsg() {
	header := msg.Header

	fmt.Println("---------- jt808msg ----------")
	fmt.Printf("msgId = 0x%04x\n", header.MsgId)
	fmt.Printf("deviceId = %s\n", header.DeviceId)
	fmt.Printf("bodyLen = %d\n", header.BodyLen)
	fmt.Printf("encrypt = 0x%02x\n", header.EncryptMode)
	fmt.Printf("serialNum = 0x%02x\n", header.SerialNum)
	fmt.Println("hasSub = ", header.HasSubPkg)

	if header.HasSubPkg {
		fmt.Printf("pkgNum = 0x%02x\n", header.PkgNum)
		fmt.Printf("totalPkg = 0x%02x\n", header.TotalPkg)
	}

	fmt.Println("Body raw data:")
	PrintByteArray(msg.BodyRawData)
	fmt.Println("Raw data:")
	PrintByteArray(msg.RawData)

	fmt.Println("------------------------------")
}

func CheckJT808Data(data []byte) bool {
	x := data[0]
	checkCode := data[len(data)-1]

	for i := 1; i < len(data)-1; i++ {
		x ^= data[i]
	}

	fmt.Printf("expected checkcode = %02x, real = %02x\n", checkCode, x)

	return x == checkCode
}

func PrintByteArray(data []byte) {
	for i := 0; i < len(data); i++ {
		fmt.Printf("%02x ", data[i])
	}
	fmt.Println()
}

func startSocketTimer(timer *time.Timer, conn *net.Conn) {
	<-timer.C
	(*conn).Close()
}

func resetSocketTimer(timer *time.Timer) {
	timer.Reset(time.Minute * 1)
}

func handleConn(conn *net.Conn) {
	reader := bufio.NewReader(*conn)

	buf := make([]byte, 0)
	bodyBuf := make([]byte, 0)
	flag := false
	timer := time.NewTimer(time.Minute * 1)

	go startSocketTimer(timer, conn)

	for {
		ch, err := reader.ReadByte()

		if err != nil {
			return
		}

		if !flag { // 还没进入到读数据帧
			if ch == 0x7e { // 数据开头
				for { // 跳过开头所有的 0x7e
					ch, err = reader.ReadByte()

					if err != nil {
						return
					}

					if ch != 0x7e {
						break
					}
				}

				flag = true
			} else {
				continue
			}
		}

		// 正文处理
		// 处理转义
		if ch == 0x7d {
			ch, err := reader.ReadByte() // 预先往前读一位

			if err != nil {
				log.Println("Error read after 0x7d.")
				break
			}

			if ch == 0x02 { // 若下一个是 0x02，则说明这两个字符是 0x7e 的转义
				buf = append(buf, 0x7e)
			} else if ch == 0x01 { // 若下一个是 0x01，则说明这两个字符是 0x7d 的转义
				buf = append(buf, 0x7d)
			} else {
				log.Println("Error byte data after 0x7d.")
				break
			}
		} else if ch == 0x7e { // 数据的结尾
			flag = false
			resetSocketTimer(timer)

			jt808Header := DecodeJT808Header(buf)

			if jt808Header.HasSubPkg && jt808Header.PkgNum < jt808Header.TotalPkg {
				bodyBuf = append(bodyBuf, buf[len(jt808Header.RawData):len(buf)-1]...)
			} else {
				bodyBuf = append(bodyBuf, buf[len(jt808Header.RawData):len(buf)-1]...)

				var jt808Msg JT808Msg
				jt808Msg.RawData = buf
				jt808Msg.BodyRawData = bodyBuf
				jt808Msg.Header = jt808Header

				HandleJT808Msg(&jt808Msg, conn)

				bodyBuf = make([]byte, 0)
			}

			buf = make([]byte, 0)
		} else {
			buf = append(buf, ch)
		}
	}

	(*conn).Close()
}

func main() {
	initInfluxDB()
	defer influxClient.Close()

	listener, err := net.Listen("tcp", ":16901")

	if err != nil {
		log.Fatal(err.Error())
	}

	defer listener.Close()
	log.Println("Server started!")

	for {
		conn, err := listener.Accept()

		if err != nil {
			log.Println(err.Error())
			continue
		}

		go handleConn(&conn)
	}
}

